/**
 * @(#)Logic.java
 *
 * Copyright: Copyright (c) 2003 Carnegie Mellon University
 *
 * This program is the implementation of the original system in the Assignment 2 of "Architectures
 * of Software Systems" class. For the details of each classes, please refer to the provided javadoc
 * documents. You will also find some useful information such as implementation decisions, coding
 * standards, and naming conventions in the README file.
 */


import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;


/**
 * This class represents a logic node which is responsible for providing high-level data access
 * methods to clients using RMI mechanism. It depends on the functionalities of data node
 * which is made available also using RMI mechanism, thus acting as a "bridge" between client nodes
 * and the data node.
 *
 * @author Jung Soo Kim
 * @version 1.0
 */
public class Logic extends UnicastRemoteObject implements RILogic {

    private static final String LOGIC_NAME = "Logic";
    private static final String DATA_NAME  = "Data";

    /**
     * Remote reference to data node.
     */
    protected RIData rmiDataNode;
    protected String sLoginID = "";

    /**
     * Constructs a logic node. A remote reference to the data node is acquired at the time of
     * creation.
     *
     * @param sDataName the name of data node
     */
    public Logic(String sDataName)
           throws RemoteException, NotBoundException, MalformedURLException {
        // Get the remote reference to data node.
        this.rmiDataNode = (RIData) Naming.lookup(sDataName);
    }

    public void setLoginID(String sUserID) 
	        throws RemoteException {
    	
    	sLoginID = sUserID;
    }
    /**
     * List all students.
     *
     * @return a string result of command processing
     */
    public String getAllStudents()
                  throws RemoteException {
        // Get all student records.
        ArrayList vStudent = this.rmiDataNode.getAllStudentRecords();

        // Construct a list of student information and return it.
        String sReturn = "";
        for (int i=0; i<vStudent.size(); i++) {
            sReturn += (i == 0 ? "" : "\n") + ((Student) vStudent.get(i)).toString();
        }
        
        System.out.println("LoginID : " +  sLoginID);
        
        return sReturn;
    }

    /**
     * List all courses.
     *
     * @return a string result of command processing
     */
    public String getAllCourses()
                  throws RemoteException {
        // Get all course records.
        ArrayList vCourse = this.rmiDataNode.getAllCourseRecords();

        // Construct a list of course information and return it.
        String sReturn = "";
        for (int i=0; i<vCourse.size(); i++) {
            sReturn += (i == 0 ? "" : "\n") + ((Course) vCourse.get(i)).toString();
        }
        return sReturn;
    }

    /**
     * List students who registered for a course.
     *
     * @param sCID a string representing course ID
     * @param sSection a string representing course section
     * @return a string result of command processing
     */
    public String getRegisteredStudents(String sCID, String sSection)
                  throws RemoteException {
        // Get the list of students who registered for the given course.
        Course objCourse = this.rmiDataNode.getCourseRecord(sCID, sSection);
        if (objCourse == null) {
            return "Invalid course ID or course section";
        }
        ArrayList vStudent = objCourse.getRegisteredStudents();

        // Construct a list of student information and return it.
        String sReturn = "";
        for (int i=0; i<vStudent.size(); i++) {
            sReturn += (i == 0 ? "" : "\n") + ((Student) vStudent.get(i)).toString();
        }
        return sReturn;
    }

    /**
     * List courses a student has registered for.
     *
     * @param sSID a string representing student ID
     * @return a string result of command processing
     */
    public String getRegisteredCourses(String sSID)
                  throws RemoteException {
        // Get the list of courses the given student has registered for.
        Student objStudent = this.rmiDataNode.getStudentRecord(sSID);
        if (objStudent == null) {
            return "Invalid student ID";
        }
        ArrayList vCourse = objStudent.getRegisteredCourses();

        // Construct a list of course information and return it.
        String sReturn = "";
        for (int i=0; i<vCourse.size(); i++) {
            sReturn += (i == 0 ? "" : "\n") + ((Course) vCourse.get(i)).toString();
        }
        return sReturn;
    }

    /**
     * List courses a student has completed.
     *
     * @param sSID a string representing student ID
     * @return a string result of command processing
     */
    public String getCompletedCourses(String sSID)
                  throws RemoteException {
        // Get the list of courses the given student has completed.
        Student objStudent = this.rmiDataNode.getStudentRecord(sSID);
        if (objStudent == null) {
            return "Invalid student ID";
        }
        ArrayList vCourseID = objStudent.getCompletedCourses();

        // Construct a list of course information and return it.
        String sReturn = "";
        for (int i=0; i<vCourseID.size(); i++) {
            String sCID = (String) vCourseID.get(i);
            String sName = this.rmiDataNode.getCourseName(sCID);
            sReturn += (i == 0 ? "" : "\n") + sCID + " " + (sName == null ? "Unknown" : sName);
        }
        return sReturn;
    }

    /**
     * Register a student for a course. Conflict check is done before making a registration.
     *
     * @param sSID a string representing student ID
     * @param sCID a string representing course ID
     * @param sSection a string representing course section
     * @return a string result of command processing
     */
    public String makeARegistration(String sSID, String sCID, String sSection)
                  throws RemoteException {
        // Get the student and course records.
        Student objStudent = this.rmiDataNode.getStudentRecord(sSID);
        Course objCourse = this.rmiDataNode.getCourseRecord(sCID, sSection);
        if (objStudent == null) {
            return "Invalid student ID";
        }
        if (objCourse == null) {
            return "Invalid course ID or course section";
        }

        // Check if the given course conflicts with any of the courses the student has registered.
        ArrayList vCourse = objStudent.getRegisteredCourses();
        for (int i=0; i<vCourse.size(); i++) {
            if (((Course) vCourse.get(i)).conflicts(objCourse)) {
                return "Registration cconflicts";
            }
        }

        // Request validated. Proceed to register.
        this.rmiDataNode.makeARegistration(sSID, sCID, sSection);
        return "Successful!";
    }

    /**
     * Creates a logic node and starts it. Two parameters are expected, the first one as data node
     * name and the second one as logic node name.
     *
     * @param args array of input parameters
     */
    public static void main(String args[]) {
        // Check the number of parameters.
        if (args.length != 0) {
            System.out.println("Incorrect number of parameters");
            System.out.println("Usage: java Logic");
            System.exit(1);
        }

        try {
            // Create a logic node and make it available through RMI.
            Logic objLogic = new Logic(DATA_NAME);
            Naming.rebind(LOGIC_NAME, objLogic);
            System.out.println("Logic node is ready to serve.");

            // Wait for user interrupt.
            System.out.println("Press Enter to terminate.");
            System.in.read();

            // Clean up and finish.
            Naming.unbind(LOGIC_NAME);
            System.out.println("Logic node is leaving, bye. Press ctrl-c if the delay is too long.");
        }
        catch (java.rmi.ConnectException e) {
            // Show error message and quit.
            System.err.println("Java RMI error: check if rmiregistry is running.");
            System.exit(1);
	}
        catch (java.rmi.NotBoundException e) {
            // Show error message and quit.
            System.err.println("Java RMI error: check if data node is running.");
            System.exit(1);
        }
        catch (Exception e) {
            // Dump the exception information for debugging.
            System.out.println("Unexpected exception at " + LOGIC_NAME);
            e.printStackTrace();
            System.exit(1);
        }
    }
}
